#include "file_server.h"

#include <string.h>
#include <sys/stat.h>

#include "file_io.h"
#include "log.h"
#include "uuid.h"

namespace chat {
// FileServerImpl

bool FileServerImpl::createStoragePath() {
    umask(0);
    if (mkdir(file_storage_path_.c_str(), 0777) == -1) {
        if (errno != EEXIST) {
            LOG_FATAL("{} 目录创建失败: {}", file_storage_path_,
                      strerror(errno));

            return false;
        }
    }

    return true;
}

void FileServerImpl::GetSingleFile(
    ::google::protobuf::RpcController* controller,
    const ::chat::GetSingleFileReq* request, ::chat::GetSingleFileRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    response->set_request_id(request->request_id());

    LOG_DEBUG("{}: 收到文件下载请求", request->request_id());

    std::string filename = file_storage_path_ + request->file_id();
    std::string content;
    if (File::read(filename, &content) == false) {
        LOG_ERROR("{} 文件下载失败", filename);

        response->set_success(false);
        response->set_errmsg("文件下载失败");
    } else {
        LOG_DEBUG("{} 文件下载成功", filename);

        response->set_success(true);
        response->set_errmsg("OK");
        FileDownloadData* data = response->mutable_file_data();
        data->set_file_id(request->file_id());
        data->set_file_content(content);

        LOG_DEBUG("{}: 成功处理文件下载请求", request->request_id());
    }
}

void FileServerImpl::GetMultiFile(::google::protobuf::RpcController* controller,
                                  const ::chat::GetMultiFileReq* request,
                                  ::chat::GetMultiFileRsp* response,
                                  ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("{}: 收到文件下载请求", request->request_id());

    response->set_request_id(request->request_id());
    auto file_map = response->mutable_file_data();

    for (int i = 0; i < request->file_id_list_size(); ++i) {
        std::string file_id = request->file_id_list(i);
        std::string filename = file_storage_path_ + file_id;
        std::string content;
        if (File::read(filename, &content) == false) {
            LOG_ERROR("{} 文件下载失败", filename);

            response->set_success(false);
            response->set_errmsg(filename + "文件下载失败");

            return;
        } else {
            LOG_DEBUG("{} 文件下载成功", filename);

            FileDownloadData data;
            data.set_file_id(file_id);
            data.set_file_content(content);

            file_map->insert({file_id, data});
        }
    }

    response->set_success(true);
    response->set_errmsg("OK");

    LOG_DEBUG("{}: 成功处理文件下载请求", request->request_id());
}

void FileServerImpl::PutSingleFile(
    ::google::protobuf::RpcController* controller,
    const ::chat::PutSingleFileReq* request, ::chat::PutSingleFileRsp* response,
    ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    LOG_DEBUG("{}: 收到文件上传请求", request->request_id());

    response->set_request_id(request->request_id());

    const FileUploadData& data = request->file_data();
    std::string file_id = Uuid::uuid();
    std::string filename = file_storage_path_ + file_id;

    if (!File::write(filename, data.file_content())) {
        LOG_ERROR("{} 文件上传失败", data.file_name());

        response->set_success(false);
        response->set_errmsg(data.file_name() + "文件上传失败");
    } else {
        FileMessageInfo* info = response->mutable_file_info();
        info->set_file_id(file_id);
        info->set_file_contents(data.file_content());
        info->set_file_size(data.file_size());
        info->set_file_name(data.file_name());

        response->set_success(true);
        response->set_errmsg("OK");

        LOG_DEBUG("{}: 成功处理文件上传请求", request->request_id());
    }
}

void FileServerImpl::PutMultiFile(::google::protobuf::RpcController* controller,
                                  const ::chat::PutMultiFileReq* request,
                                  ::chat::PutMultiFileRsp* response,
                                  ::google::protobuf::Closure* done) {
    brpc::ClosureGuard closure_guard(done);

    response->set_request_id(request->request_id());

    for (int i = 0; i < request->file_data_size(); ++i) {
        const FileUploadData& data = request->file_data(i);

        std::string file_id = Uuid::uuid();
        std::string filename = file_storage_path_ + file_id;

        if (!File::write(filename, data.file_content())) {
            LOG_ERROR("{} 文件上传失败", data.file_name());

            response->set_success(false);
            response->set_errmsg(data.file_name() + "文件上传失败");

            return;
        } else {
            FileMessageInfo* info = response->add_file_info();

            info->set_file_id(file_id);
            info->set_file_contents(data.file_content());
            info->set_file_size(data.file_size());
            info->set_file_name(data.file_name());
        }
    }

    response->set_success(true);
    response->set_errmsg("OK");
}

// -------------------------------------------------------------------

// FileServer

void FileServer::start() { brpc_server_->RunUntilAskedToQuit(); }

// -------------------------------------------------------------------

// FileServerBuilder

void FileServerBuilder::makeRegisterClient(const std::string& register_name,
                                           const std::string& etcd_host,
                                           const std::string& access_host) {
    register_client_ = std::make_shared<Registry>(etcd_host);

    register_client_->registry(register_name, access_host);
}

void FileServerBuilder::makeBrpcServer(const uint16_t prot,
                                       const int idle_timeout_sec,
                                       const int num_threads,
                                       const std::string file_storage_path) {
    brpc_server_ = std::make_shared<brpc::Server>();

    // 1. 向服务器中，新增FileService服务
    FileServerImpl* file_service = new FileServerImpl(file_storage_path);
    if (file_service->createStoragePath() == false) {
        LOG_FATAL("{} 目录创建失败", file_storage_path);
        abort();
    }
    if (-1 == brpc_server_->AddService(
                  file_service, brpc::ServiceOwnership::SERVER_OWNS_SERVICE)) {
        LOG_ERROR("RPC添加服务失败");
        abort();
    }

    // 3. 启动服务器
    brpc::ServerOptions options;
    options.idle_timeout_sec = idle_timeout_sec;
    options.num_threads = num_threads;

    if (-1 == brpc_server_->Start(prot, &options)) {
        LOG_ERROR("RPC服务启动失败");
        abort();
    }
}

FileServerPtr FileServerBuilder::build() {
    if (register_client_ == nullptr) {
        LOG_FATAL("服务注册客户端未实例化");
        abort();
    }
    if (brpc_server_ == nullptr) {
        LOG_FATAL("RPC服务未实例化");
        abort();
    }

    return std::make_shared<FileServer>(register_client_, brpc_server_);
}
}  // namespace chat